package com.treepoke.util.ldap;

import com.treepoke.util.criptografia.Criptografia;
import com.treepoke.util.exception.UtilException;
import com.treepoke.util.logging.CRCCLogger;
import com.treepoke.util.properties.TPConstant;
import com.novell.ldap.LDAPAttribute;
import com.novell.ldap.LDAPConnection;
import com.novell.ldap.LDAPException;
import com.novell.ldap.LDAPModification;
import com.novell.ldap.connectionpool.PoolManager;
import java.sql.SQLException;

/**
 * Clase que permite interactuar con un servidor LDAP usando la librería 
 * Novell LDAP Classes for Java
 */
public class LDAPUtil {

    private static LDAPUtil instance;
    private PoolManager poolManager = null;
    private CRCCLogger logger = CRCCLogger.getLogger(TPConstant.getNOMBRE_APLICACION());

    private LDAPUtil() throws UtilException {
        try {
            poolManager = new PoolManager(TPConstant.getLDAP_HOST(), 
                Integer.parseInt(TPConstant.getLDAP_PORT()), 
                Integer.parseInt(TPConstant.getLDAP_MAX_CONNS()), 
                Integer.parseInt(TPConstant.getLDAP_MAX_SHARED_CONNS()), null);
        } catch (LDAPException ex) {
            throw new UtilException("Error instanciando poolManager" + TPConstant.getLDAP_HOST() + 
                ":" + TPConstant.getLDAP_PORT(), ex, 0);
        }        
    }

    /**
     * Método que recupera la instancia del
     * <code>LDAPConnectionManager</code>.
     * @return
     * @throws UtilException
     */
    static public LDAPUtil getInstance() throws UtilException {
        if (instance == null) {
            instance = new LDAPUtil();
        }

        return instance;
    }

    /**
     * Obtiene una conexión al LDAP
     * @return
     * @throws UtilException
     */
    public LDAPConnection getConnection() throws UtilException {
        try {
            return poolManager.getBoundConnection(TPConstant.getLDAP_USER(), 
                Criptografia.decrypt(TPConstant.getLDAP_PASSWORD()).getBytes("UTF8"));
        } catch (Exception ex) {
            throw new UtilException("Error obteniendo conexión: " + ex.getMessage(), ex, 0);
        }
    }

    /**
     * Metodo que vuelve disponible la conexión al LDAP
     * @param connection Define la conexión que se volverá disponible
     * @throws SQLException
     */
    public void makeConnectionAvailable(LDAPConnection connection) throws UtilException {
        if (connection != null) {
            poolManager.makeConnectionAvailable(connection);
        }
    }
    
    /**
     * Agrega un atributo a la entrada del LDAP identificada por su DN
     * @param connection
     * @param nombreAtributo
     * @param valorAtributo
     * @throws UtilException 
     */
    public void agregarAtributo(LDAPConnection connection, String entradaDN,
        String nombreAtributo, String valorAtributo) 
    throws UtilException {
        gestionarAtributo(connection, LDAPModification.ADD, entradaDN, 
            nombreAtributo, valorAtributo);
    }
    
    /**
     * Elimina un atributo a la entrada del LDAP identificada por su DN
     * @param connection
     * @param nombreAtributo
     * @param valorAtributo
     * @throws UtilException 
     */
    public void eliminarAtributo(LDAPConnection connection, String entradaDN,
        String nombreAtributo, String valorAtributo) 
    throws UtilException {
        
        gestionarAtributo(connection, LDAPModification.DELETE, entradaDN, 
            nombreAtributo, valorAtributo);
    }
    
    /**
     * Reemplaza un atributo a la entrada del LDAP identificada por su DN
     * @param connection
     * @param nombreAtributo
     * @param valorAtributo
     * @throws UtilException 
     */
    public void reemplazarAtributo(LDAPConnection connection, String entradaDN,
        String nombreAtributo, String valorAtributo) 
    throws UtilException {
        gestionarAtributo(connection, LDAPModification.REPLACE, entradaDN, 
            nombreAtributo, valorAtributo);
    }    
    
    /**
     * Gestiona (agregar, elimina o modifica) un atributo a la entrada del LDAP 
     * identificada por su DN
     * @param connection
     * @param nombreAtributo
     * @param valorAtributo
     * @throws UtilException 
     */    
    private void gestionarAtributo(LDAPConnection connection, int tipoModificacion,
        String entradaDN, String nombreAtributo, String valorAtributo) 
    throws UtilException {
        if (connection == null) {
            throw new UtilException("La conexión no puede ser nula.", 0);
        } else if (!connection.isConnectionAlive()) {
            throw new UtilException("La conexión no está viva.", 0);
        } else if (!connection.isBound()) {
            throw new UtilException("La conexión no está autenticada.", 0);
        }
        
        logger.debugMessage("LDAPUtil.gestionarAtributo: tipoModificacion=" + tipoModificacion +
            ", entradaDN=" + entradaDN + ", nombreAtributo=" + nombreAtributo +
            ", valorAtributo=" + valorAtributo);
        
        try {
            connection.modify(entradaDN, new LDAPModification(tipoModificacion, 
                new LDAPAttribute(nombreAtributo, valorAtributo)));
        } catch (LDAPException ex) {
            throw new UtilException("Error gestionando atributo: " + ex.getMessage(), ex, 0);
        }
    }    

    public void finalizar() {
        instance = null;
    }
    
}
